package io.kiki.sba.registry.server.clientInterface.resource;

import io.kiki.sba.registry.api.exchange.NodeExchanger;
import io.kiki.sba.registry.api.exchange.message.SimpleRequest;
import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.Response;
import io.kiki.sba.registry.common.model.console.PersistenceData;
import io.kiki.sba.registry.common.model.console.PersistenceDataBuilder;
import io.kiki.sba.registry.common.model.constants.ValueConstants;
import io.kiki.sba.registry.common.model.sessionserver.StopPushRequest;
import io.kiki.sba.registry.common.model.store.Url;
import io.kiki.sba.registry.remoting.http.HttpClient;
import io.kiki.sba.registry.server.clientInterface.bootstrap.Config;
import io.kiki.sba.registry.server.clientInterface.bootstrap.ExecutorManager;
import io.kiki.sba.registry.server.clientInterface.providedata.FetchStopPushService;
import io.kiki.sba.registry.server.shared.meta.MetaServerService;
import io.kiki.sba.registry.server.shared.resource.AuthChecker;
import io.kiki.sba.registry.server.shared.util.PersistenceDataParser;
import io.kiki.sba.registry.store.api.meta.ProvideDataRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ws.rs.*;
import javax.ws.rs.client.Client;
import javax.ws.rs.core.MediaType;
import java.util.List;
import java.util.Map;


@Path("api/emergency")
@Produces(MediaType.APPLICATION_JSON)
public class EmergencyApiResource {

    protected static final String META_LEADER_HEALTHY_URL = "http://%s:9615/health/check";
    private static final Logger logger = LoggerFactory.getLogger(EmergencyApiResource.class);
    @Autowired
    protected ProvideDataRepository provideDataRepository;
    @Autowired
    protected MetaServerService metaServerService;
    @Autowired
    protected Config config;
    @Autowired
    protected ExecutorManager executorManager;
    @Autowired
    private NodeExchanger sessionConsoleExchanger;
    @Resource
    private FetchStopPushService fetchStopPushService;
    private Client client;

    @PostConstruct
    public void init() {
        client = HttpClient.getInstance().getClient();
    }

    /**
     * close push by repository when meta leader is down
     */
    @POST
    @Path("/pushSwitch/stop")
    @Produces(MediaType.APPLICATION_JSON)
    public Response closePushByRepository() {
        boolean leaderHealthy = leaderHealthy();
        boolean renewSuccess = metaServerService.renewNode();
        logger.info("[closePushByRepository]leaderHealthy: {}, renewSuccess: {}", leaderHealthy, renewSuccess);
        if (leaderHealthy || renewSuccess) {
            logger.error("leader is healthy, should not stop server by session.");
            return Response.buildFailedResponse("leader is healthy, should not close push by session.");
        }

        PersistenceData update = PersistenceDataBuilder.createPersistenceDataForBool(ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID, true);
        provideDataRepository.put(update);
        return Response.buildSuccessResponse();
    }

    /**
     * stop server by repository when meta leader is down
     */
    @POST
    @Path("/shutdown")
    @Produces(MediaType.APPLICATION_JSON)
    public Response shutdownByRepository(@FormParam("token") String token) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("update shutdownSwitch, stop=true auth check={} fail!", token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (!isStopPush()) {
            logger.error("push switch is open");
            return GenericResponse.buildFailedResponse("push switch is open");
        }

        boolean leaderHealthy = leaderHealthy();
        boolean renewSuccess = metaServerService.renewNode();
        logger.info("[shutdownByRepository]leaderHealthy: {}, renewSuccess: {}", leaderHealthy, renewSuccess);
        if (leaderHealthy || renewSuccess) {
            logger.error("leader is healthy, should not stop server by session.");
            return Response.buildFailedResponse("leader is healthy, should not stop server by session.");
        }

        PersistenceData update = PersistenceDataBuilder.createPersistenceDataForBool(ValueConstants.SHUTDOWN_SWITCH_DATA_ID, true);
        provideDataRepository.put(update);
        return Response.buildSuccessResponse();
    }

    /**
     * close push broadcast on zone when meta leader is down
     */
    @GET
    @Path("/pushSwitch/zone/stop")
    @Produces(MediaType.APPLICATION_JSON)
    public Response zoneClosePush() {
        fetchStopPushService.setStopPushSwitch(System.currentTimeMillis(), true);
        List<Url> servers = Sdks.getOtherConsoleServers(null, config, metaServerService);
        if (servers.size() == 0) {
            return Response.buildSuccessResponse();
        }

        Map<Url, Response> map = Sdks.concurrentSdkSend(executorManager.getZoneSdkExecutor(), servers, (Url url) -> {
            final StopPushRequest req = new StopPushRequest(true);
            return (Response) sessionConsoleExchanger.request(new SimpleRequest(req, url)).getResult();
        }, 3000);
        return Sdks.getFailedResponseIfAbsent(map.values());
    }

    private boolean leaderHealthy() {
        String leader = metaServerService.getMetaServerLeader();
        String url = String.format(META_LEADER_HEALTHY_URL, leader);
        try {
            javax.ws.rs.core.Response resp = client.target(url).request().buildGet().invoke();
            if (resp.getStatus() != javax.ws.rs.core.Response.Status.OK.getStatusCode()) {
                logger.error("[leaderHealthy] failed to query leader status from url: {}, resp status: {}", url, resp.getStatus());
                return false;
            }
            Response response = new Response();
            response = resp.readEntity(response.getClass());
            logger.info("[leaderHealthy]url: {}, resp: {}", url, response);
            return response.isSuccess();
        } catch (Throwable t) {
            logger.error("[leaderHealthy] failed to query leader status from url: {}", url, t);
        }
        return false;
    }

    private boolean isStopPush() {
        PersistenceData stopPushResp = provideDataRepository.get(ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID);
        return PersistenceDataParser.parse2BoolIgnoreCase(stopPushResp, false);
    }


    public EmergencyApiResource setMetaNodeService(MetaServerService metaServerService) {
        this.metaServerService = metaServerService;
        return this;
    }
}
